/*
 * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xuexiang.xdlink.component.channel.impl.socket.server;

import android.os.Looper;

import com.xuexiang.xdlink.component.channel.impl.AbstractChannel;
import com.xuexiang.xdlink.exception.ChannelException;
import com.xuexiang.xdlink.logs.DCFLog;
import com.xuexiang.xdlink.util.ConvertTools;
import com.xuexiang.xdlink.util.DCFExecutors;
import com.xuexiang.xdlink.util.Utils;

import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Callable;

/**
 * 抽象的服务socket 通信通道
 *
 * @author xuexiang
 * @since 2018/9/5 上午9:50
 */
public abstract class AbstractServerSocketChannel extends AbstractChannel {

    ServerSocket mServerSocket;
    Socket mSocket = null;

    /**
     * 服务开启的端口号
     */
    int mPort;

    /**
     * 是否自动切换线程
     */
    private boolean mIsAutoSwitchThread;

    /**
     * 构造函数
     *
     * @param port 服务开启的端口号
     */
    public AbstractServerSocketChannel(int port) {
        this(port, true);
    }

    /**
     * 构造函数
     *
     * @param port               服务开启的端口号
     * @param isAutoSwitchThread 是否自动切换线程
     */
    public AbstractServerSocketChannel(int port, boolean isAutoSwitchThread) {
        mPort = port;
        mIsAutoSwitchThread = isAutoSwitchThread;
    }

    @Override
    public void openChannel() {
        if (mServerSocket != null || mSocket != null) return;

        DCFExecutors.get().addTask(new Runnable() {
            @Override
            public void run() {
                startServer();
            }
        });
    }

    /**
     * 启动服务
     */
    protected abstract void startServer();

    @Override
    public void closeChannel() {
        close();
    }

    @Override
    protected void close() {
        super.close();

        Utils.closeIO(mServerSocket);
        mServerSocket = null;
    }

    @Override
    public void write(final byte[] message) throws ChannelException {
        if (isMainThread()) {
            if (mIsAutoSwitchThread) {
                DCFExecutors.get().addTask(new Callable<Boolean>() {
                    @Override
                    public Boolean call() throws Exception {
                        writeBytes(message);
                        return true;
                    }
                });
            } else {
                throw new ChannelException(ChannelException.WRITE_ON_MAIN_THREAD_ERROR, "Socket[server] write error in main thread：" + ConvertTools.bytes2HexString(message));
            }
        } else {
            writeBytes(message);
        }
    }

    private boolean isMainThread() {
        return Looper.getMainLooper() == Looper.myLooper();
    }

    /**
     * 写bytes
     *
     * @param message 消息
     * @throws ChannelException
     */
    protected void writeBytes(byte[] message) throws ChannelException {
        writeBytes(mSocket, message);
    }

    /**
     * 写bytes
     *
     * @param message 消息
     * @throws ChannelException
     */
    protected void writeBytes(Socket socket, byte[] message) throws ChannelException {
        try {
            if (socket != null) {
                OutputStream os = socket.getOutputStream();
                os.write(message);
                os.flush();
                DCFLog.d("Socket[server] data sent successfully，data length：" + message.length);
            }
        } catch (Exception e) {
            throw new ChannelException(ChannelException.WRITE_FAILED_ERROR, e);
        }
    }

}
